using System;
using System.Activities;
using System.Activities.Presentation.Model;
using System.Activities.Presentation.View;
using System.Activities.Statements;
using System.Collections.Generic;
using Headsight.BPMN2WF.BPMN.Model;
using Headsight.BPMN2WF.BPMN.Utils;

namespace Headsight.BPMN2WF.Transformation.Transformers.Additions
{
    /// <summary>
    /// <see cref="IBPMN2WFTransformation"/> implementation responsible for 
    /// setting/carrying over <i>BPMN layout information</i> to created respective
    /// <i>WF artifacts</i>.
    /// </summary>
    public class BPMN2WFTransformationLayouter : BPMN2WFTransformationDelegateBaseImpl
    {
        private readonly IDictionary<String, BPMNElementVisual> _originalBpmnModelsAlignmentMetaData;

        public BPMN2WFTransformationLayouter(IBPMN2WFTransformation @delegate, IDictionary<String, BPMNElementVisual> originalBpmnModelsAlignmentMetaData)
            : base(@delegate)
        {
            _originalBpmnModelsAlignmentMetaData = originalBpmnModelsAlignmentMetaData;
        }

        // collect all elements + association bpmnflow <-> activity
        public override BPMNTransformationResult Transform(IBPMN2WFTransformationContext ctx, BPMNFlowNode flowNode)
        {
            var vStateSrv = ctx.WorkflowDesigner.Context.Services.GetService<ViewStateService>();
            var mTreeMgr = ctx.WorkflowDesigner.Context.Services.GetService<ModelTreeManager>();

            var transformationResult = Delegate.Transform(ctx, flowNode);
            var result = transformationResult.Result;

            if (result is Flowchart)
            {
                var flowchart = (Flowchart)result;

                var vStateFlowchart = GetViewState(mTreeMgr, vStateSrv, (Flowchart)result, result);

                if (vStateFlowchart == null)
                {
                    var elementVisual = GetBPMNElementVisualById(flowNode.BPMNFlowNodeOrNodeIn().Id);

                    if (elementVisual != null)
                    {
                        var bounds = elementVisual.Bounds;
                        var mItem = mTreeMgr.CreateModelItem(null, result);
                        vStateSrv.StoreViewStateWithUndo(mItem, "ShapeLocation", new System.Windows.Point(bounds.X * 1.5, bounds.Y * 1.5));
                    }
                }

                foreach (var each in flowchart.Nodes)
                {
                    var allViewStates = GetViewState(mTreeMgr, vStateSrv, flowchart, each);

                    if (allViewStates != null && allViewStates.ContainsKey("ShapeLocation"))
                    {
                        var mItemFlowStep = mTreeMgr.CreateModelItem(null, each);
                        vStateSrv.StoreViewStateWithUndo(mItemFlowStep, "ShapeLocation", allViewStates["ShapeLocation"]);
                    }
                    else
                    {
                        var elementVisual = GetBPMNElementVisualById(flowNode.BPMNFlowNodeOrNodeIn().Id);

                        if (elementVisual != null)
                        {
                            var bounds = elementVisual.Bounds;
                            var mItem = mTreeMgr.CreateModelItem(null, each);
                            vStateSrv.StoreViewStateWithUndo(mItem, "ShapeLocation", new System.Windows.Point(bounds.X * 1.5, bounds.Y * 1.5));
                        }
                    }
                }
            }
            else // result is normal Activity
            {
                var elementVisual = GetBPMNElementVisualById(flowNode.BPMNFlowNodeOrNodeIn().Id);

                if (elementVisual != null)
                {
                    var bounds = elementVisual.Bounds;
                    var mItem = mTreeMgr.CreateModelItem(null, result);
                    vStateSrv.StoreViewStateWithUndo(mItem, "ShapeLocation", new System.Windows.Point(bounds.X * 1.5, bounds.Y * 1.5));
                }
            }

            return transformationResult;
        }

        private BPMNElementVisual GetBPMNElementVisualById(String id)
        {
            BPMNElementVisual result;

            _originalBpmnModelsAlignmentMetaData.TryGetValue(id, out result);

            return result;
        }

        private static IDictionary<String, Object> GetViewState(ModelTreeManager mTreeMgr, ViewStateService vStateSrv, Flowchart flowchart, Object each)
        {
            if (each is FlowStep)
            {
                return
                    GetViewState(mTreeMgr, vStateSrv, flowchart, ((FlowStep)each).Action)
                    ??
                    GetViewState(mTreeMgr, vStateSrv, flowchart, flowchart);
            }

            if (each is Activity)
            {
                var mItem = mTreeMgr.CreateModelItem(null, each);

                return vStateSrv.RetrieveAllViewState(mItem);
            }

            if (each is FlowSwitch<String>)
            {
                GetViewState(mTreeMgr, vStateSrv, flowchart, flowchart);
            }

            return null;
        }
    }
}